#ifndef GABOR
#define GABOR

#include "filter_kernel.h"
//#include <common.h>
//*********************.CU****************************************************
typedef float2 Complexgf;

// #define Threshold 200000
////////////////////////////////////////////////////////////////////////////////
// Common host and device functions
////////////////////////////////////////////////////////////////////////////////

//Round a / b to nearest higher integer value
inline int iDivUpSub(int a, int b){
    return (a % b != 0) ? (a / b + 1) : (a / b);
}


//Round a / b to nearest lower integer value
inline int iDivDown(int a, int b){
    return a / b;
}

//Align a to nearest higher multiple of b
inline int iAlignUpSub(int a, int b){
    return (a % b != 0) ?  (a - a % b + b) : a;
}


//Align a to nearest lower multiple of b
inline int iAlignDown(int a, int b){
    return a - a % b;
}
 



// Assuming ROW_TILE_W, KERNEL_RADIUS_ALIGNED and dataW 
// are multiples of coalescing granularity size,
// all global memory operations are coalesced in convolutionRowGPU()
const int  ROW_TILE_W = 128;   //was 128
const int  KERNEL_RADIUS_ALIGNED_X = 32;   // was 32
const int  KERNEL_RADIUS_PAD = iAlignUpSub(KERNEL_RADIUS_X, 16);    //was 16
//const int  KERNEL_RADIUS_PAD = iAlignUp(32, 16);

// Assuming COLUMN_TILE_W and dataW are multiples
// of coalescing granularity size, all global memory operations 
// are coalesced in convolutionColumnGPU()
const int  COLUMN_TILE_W = 16;     //was 16
const int  COLUMN_TILE_H = 48;     //was 48

const int  BLOCK_DIM = 32;  //was 16




class Gabor
{
public:

	Gabor(int DATA_W_original, int DATA_H_original,int nLevels);
	~Gabor();
	
	//odata, idata are both device memory, C language interface
	void pad_GPU(float* odata, float* idata);
	void pad_GPU_ND(float* odata, float* idata);
	void center_surround_GPU(float* odata, float* idata, Filter_kernel& h_kernel_c, Filter_kernel& h_kernel_s);
	void center_surround_GPU_ND(float* odata, float* idata, Filter_kernel& kernel_c, Filter_kernel& h_kernel_s);

	//idata, odata are both device memory, h_kernel_x, h_kernel_y, h_kernel_t are all host kernel, x, y, diagonal axis respectively, C language interface
	void gabor_GPU(Complexgf* odata, float* idata, Filter_kernel& kernel);
	

	// cut function because the image is padded first for the consideration of border condition, C language interface
	void cut_GPU(Complexgf* odata, Complexgf* idata);
	void andImage_GPU(Complexgf* odata, Complexgf* idata,Complexgf* imask);
	
	//Gaussian function
	//odata: output
	//idata: input
	//h_kernel_x, h_kernel_y, h_kernel_t: kernel in the x, y, t axis
	//tanp: 0, +1, -1, means the orientation of the gaussian fitler(the angle between the major axis of gaussian filter and x axis)
	//      0: phi = 0, or phi = pi/2
	//		+1: 0<phi<pi/2
	//      -1: pi/2<phi<pi
	//for the circular gaussian, you just need the kernel of x and y axis. So the default value of h_kernel_t and tanp is NULL and 0 respectively.
	//for the anisotropic gaussian, you need use the cal_parameter_gaussian function to caculate the paramters necessary.
	void gaussian_GPU(float* odata, float* idata, Filter_kernel& kernel);
	void gaussian_GPU_ND(float* odata, float* idata, Filter_kernel& kernel);

	////////////////////////////////////////////////////////////////////////////////
// Data configuration
////////////////////////////////////////////////////////////////////////////////
//Image width should be aligned to maximum coalesced read/write size
//for best global memory performance in both row and column filter.
//Reduce problem size to have reasonable emulation time
	int   DATA_W_original;
	int   DATA_W_original_16multi;
	int   DATA_H_original;            // size before padding
	int   DATA_W;
	int   DATA_H;            //size after padding
	int	  KERNEL_RADIUS_PADto16;
	int   DATA_W_SHIFT;    //w_size after shift once, multiple of 16, for second column filter
	int   DATA_W_SHIFT2;   //w_size after shift once, multiple of 16, for combine and demodulation

	int   DATA_SIZE_shift1;
	int   DATA_SIZE_shift2;

	int   DATA_SIZE_ori_f;   //memory size of original input, float
	int   DATA_SIZE_ori_c;   //memory size of original input, Complex
	int   DATA_SIZE_pad_f;   //memory size of padded input, float
	int   DATA_SIZE_pad_c;   //memory size of padded input, Complex

	//parameter for necessary block computing
	//calculate parameter for necessary block computing

	int filter_interval;
	int shift_interval;
	int filter_interval2;
	int shift_interval2;
	int filter_cof;

	//set up filter execution parameter
	dim3 blockGridRows;
	dim3 blockGridColumns;
	dim3 blockGridColumns2;
	dim3 threadBlockRows;
	dim3 threadBlockColumns;

	
	//set up padding execution paramter
	dim3 grid_ori;
	dim3 grid_oriND;
    
	// setup shift, modulation, demodulation execution parameters
	dim3 grid;
	dim3 gridND;
	
	dim3 grid_shift1;
	dim3 grid_shift2;
	dim3 threads;

	/*  device memory, 
	d_DataA_unpad: original image in the device
	d_DataA; result after padding and result after center-surround
	d_padimgresult: result after gabor process
	d_ResultGPU: result after cutting */
	//unsigned char
	//	*DataA_bumblebee;
	  
/*	float
		*d_DataA_unpad,
		*d_DataA;

	Complex
		*d_padimgresult,
		*d_ResultGPU;  */

private:
	
	//img          
    float
		*d_DataA_c,
		*d_Temp,		
		*d_DataA_s;

	float
		*d_DataA_padto16multi,
        *d_DataA_real,
		*d_DataA_imag,
        *d_DataB,
        *d_shift1,
        *d_Temp_shift,
        *d_shift2;
};

#endif